package com.weilele.mvvm.utils.net

import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import com.weilele.mvvm.base.livedata.isMainThread
import com.weilele.mvvm.okHttpClient
import com.weilele.mvvm.utils.coroutine.awaitAny
import com.weilele.mvvm.utils.file.mmkvGetValue
import com.weilele.mvvm.utils.file.mmkvPutValue
import com.weilele.mvvm.utils.formJsonString
import com.weilele.mvvm.utils.toJsonString
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.BufferedReader
import java.io.IOException
import java.net.URL
import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

/**
 * cookie存储
 */
@Keep
open class SimpleCookieJar : CookieJar {
    @Keep
    class CookieStoreBean {
        @SerializedName("cookies")
        var cookies: List<Cookie>? = null

        @SerializedName("url")
        var url: String? = null
    }

    private val cookieStoreMap = mutableMapOf<String?, CookieStoreBean>()

    override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
        val host = url.host
        val bean = CookieStoreBean().apply {
            this.url = url.host
            this.cookies = cookies
        }
        cookieStoreMap[host] = bean
        mmkvPutValue(host, bean.toJsonString())
    }

    override fun loadForRequest(url: HttpUrl): List<Cookie> {
        val host = url.host
        var cookieStoreBean = cookieStoreMap[host]
        if (cookieStoreBean == null) {
            mmkvGetValue(host, "").let {
                if (it.isNotBlank()) {
                    val bean = it.formJsonString<CookieStoreBean>()
                    cookieStoreMap[host] = bean
                    cookieStoreBean = bean
                }
            }
        }
        return cookieStoreBean?.cookies ?: ArrayList()
    }
}

/**
 * 发送请求，并等待Response
 */
suspend inline fun Request.awaitHttpResponse(
    client: OkHttpClient = okHttpClient,
    crossinline callBlock: ((Call) -> Unit) = { }
): Response {
    return awaitAny {
        val call = client.newCall(this)
        callBlock.invoke(call)
        it.invokeOnCancellation {
            call.cancel()
        }
        if (isMainThread) {
            call.enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    it.resumeWithException(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    it.resume(response)
                }
            })
        } else {
            try {
                it.resume(call.execute())
            } catch (e: Throwable) {
                it.resumeWithException(e)
            }
        }
    }
}

/**
 * 发送请求，并等待转换结果
 */
suspend inline fun <reified T> Request.awaitHttpResult(
    client: OkHttpClient = okHttpClient,
    checkRealType: Boolean = false/*针对泛型，是否获取真正的cls*/,
    crossinline callBlock: ((Call) -> Unit) = { }
): T {
    return awaitHttpResponse(client, callBlock).fromJson(checkRealType)
}

/**
 * 结果转换
 */
inline fun <reified T> Response.fromJson(checkRealType: Boolean = false/*针对泛型，是否获取真正的cls*/): T {
    val bodyStr = this.body?.string()
    if (bodyStr.isNullOrBlank()) {
        throw Exception(this.message)
    } else {
        return bodyStr.formJsonString(checkRealType)
    }
}

/**
 * 发起http请求
 */
suspend inline fun <reified T> String.awaitHttp(
    requestBody: RequestBody? = null/*不为null，就代表post请求*/,
    client: OkHttpClient = okHttpClient,
    checkRealType: Boolean = false/*针对泛型，是否获取真正的cls*/,
    crossinline callBlock: ((Call) -> Unit) = { },
    requestBuildBlock: ((Request.Builder) -> Request.Builder) = { it }
): T {
    return awaitHttp(
        Request.Builder().url(this),
        requestBody,
        client,
        checkRealType,
        callBlock,
        requestBuildBlock
    )
}

/**
 * 发起http请求
 */
suspend inline fun <reified T> URL.awaitHttp(
    requestBody: RequestBody? = null/*不为null，就代表post请求*/,
    client: OkHttpClient = okHttpClient,
    checkRealType: Boolean = false/*针对泛型，是否获取真正的cls*/,
    crossinline callBlock: ((Call) -> Unit) = { },
    requestBuildBlock: ((Request.Builder) -> Request.Builder) = { it }
): T {
    return awaitHttp(
        Request.Builder().url(this),
        requestBody,
        client,
        checkRealType,
        callBlock,
        requestBuildBlock
    )
}

/**
 * 发起http请求
 */
suspend inline fun <reified T> HttpUrl.awaitHttp(
    requestBody: RequestBody? = null/*不为null，就代表post请求*/,
    client: OkHttpClient = okHttpClient,
    checkRealType: Boolean = false/*针对泛型，是否获取真正的cls*/,
    crossinline callBlock: ((Call) -> Unit) = { },
    requestBuildBlock: ((Request.Builder) -> Request.Builder) = { it }
): T {
    return awaitHttp(
        Request.Builder().url(this),
        requestBody,
        client,
        checkRealType,
        callBlock,
        requestBuildBlock
    )
}

/**
 * 发起http请求
 */
suspend inline fun <reified T> awaitHttp(
    build: Request.Builder,
    requestBody: RequestBody? = null/*不为null，就代表post请求*/,
    client: OkHttpClient = okHttpClient,
    checkRealType: Boolean = false/*针对泛型，是否获取真正的cls*/,
    crossinline callBlock: ((Call) -> Unit) = { },
    requestBuildBlock: ((Request.Builder) -> Request.Builder) = { it }
): T {
    return build
        .also {
            if (requestBody != null) {
                it.post(requestBody)
            } else {
                it.get()
            }
            requestBuildBlock.invoke(it)
        }.build()
        .awaitHttpResult(client, checkRealType, callBlock)
}

/**
 * 创建RequestBody
 */
fun HashMap<String, String>.toRequestFormBody(): RequestBody {
    val formBody = FormBody.Builder()
    for ((key, value) in this) {
        formBody.add(key, value)
    }
    formBody.build()
    return formBody.build()
}

/**
 * 创建RequestBody
 */
fun MutableMap<String, String>.toRequestFormBody(): RequestBody {
    val formBody = FormBody.Builder()
    for ((key, value) in this) {
        formBody.add(key, value)
    }
    formBody.build()
    return formBody.build()
}

/**
 * 创建RequestBody
 */
fun Any.toRequestJsonBody(): RequestBody {
    val json = when (this) {
        is String -> {
            this
        }
        else -> {
            this.toJsonString()
        }
    }
    return json.toRequestBody("application/json".toMediaTypeOrNull())
}

/**
 * 创建RequestBody
 */
fun ResponseBody?.getStringBody(): String? {
    this ?: return null
    val bufferedReader = BufferedReader(this.charStream())
    return bufferedReader.readText()
}

fun Response?.getStringBody(): String? {
    return this?.body?.getStringBody()
}